package com.yc.cloud.openai.portal.dao.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.yc.cloud.common.basic.exception.Asserts;
import com.yc.cloud.common.basic.utils.PathUtils;
import com.yc.cloud.openai.portal.common.PortalErrorTips;
import com.yc.cloud.openai.portal.config.ComfyUIProperties;
import com.yc.cloud.openai.portal.dao.ComfyUiClient;
import com.yc.cloud.openai.portal.dao.ModelClient;
import com.yc.cloud.openai.portal.dao.TextToVideoClient;
import com.yc.cloud.openai.portal.dao.VideoClient;
import com.yc.cloud.openai.portal.dto.PromptDto;
import com.yc.cloud.openai.portal.dto.QueueDto;
import com.yc.cloud.openai.portal.dto.UploadImageDto;
import com.yc.cloud.openai.portal.dto.request.*;
import com.yc.cloud.openai.portal.dto.response.*;
import com.yc.cloud.openai.portal.dto.video.request.ImageToVideoRequest;
import com.yc.cloud.openai.portal.enums.PrompOutputType;
import com.yc.cloud.openai.portal.enums.PromptType;
import com.yc.cloud.openai.portal.service.WorkflowProcessService;
import com.yc.cloud.openai.portal.utils.WebClientUtil;
import com.yc.cloud.openai.portal.vo.HistoryVo;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Component("COMFY_UI")
@Slf4j
public class ComfyUiClientImpl implements ComfyUiClient, TextToVideoClient, ModelClient, VideoClient {

    private final WebClientUtil webClientUtil;
    private final String URL;
    public static final String FLUX_TEXT_TO_IMAGE_WORKFLOW = "flux_text2image.json";
    public static final String HUNYUAN_TEXT_TO_VIDEO_WORKFLOW = "hy_text2video.json";
    public static final String SD_TEXT_TO_IMAGE_WORKFLOW = "sd_text2image.json";
    public static final String HUNYUAN_IMAGE_TO_MODEL_WORKFLOW = "hy_image2model.json";
    public static final String HUNYUAN_TEXT_TO_MODEL_WORKFLOW = "hy_text2model.json";
    public static final String WAN_IMAGE_TO_VIDEO_WORKFLOW = "wan_image2video.json";
    private final PathUtils pathUtils;
    private final ComfyUIProperties comfyUIProperties;
    private final WorkflowProcessService workflowProcessService;
    // 视频帧率,默认21
    private final int VIDEO_FRAME_RATE = 21;
    // private final String PREFIX_LORA_FOLDER = "openai/";

    public ComfyUiClientImpl(WebClientUtil webClientUtil, PathUtils pathUtils, ComfyUIProperties comfyUIProperties,
            WorkflowProcessService workflowProcessService) {
        this.webClientUtil = webClientUtil;
        this.pathUtils = pathUtils;
        this.comfyUIProperties = comfyUIProperties;
        this.workflowProcessService = workflowProcessService;
        URL = comfyUIProperties.getHttpUrl();
    }

    /**
     * 执行ComfyUi任务
     *
     * @param prompt   任务内容
     * @param clientId 客户端ID
     * @return 执行结果
     */
    private PromptDto execute(String prompt, String clientId) {
        JSONObject requestBody = new JSONObject();
        val promptObject = new JSONObject(prompt);
        requestBody.putOpt("prompt", promptObject);
        requestBody.putOpt("client_id", clientId);
        log.info("请求参数：{}", JSONUtil.toJsonPrettyStr(requestBody));
        val result = webClientUtil.postJSON(URL + "/prompt", JSONUtil.toJsonPrettyStr(requestBody), String.class);
        log.info("生成图片结果：{}", result);
        val comfyUiPromptResponse = JSONUtil.toBean(result, ComfyUiPromptResponse.class);
        return convertExecuteResult(clientId, comfyUiPromptResponse);
    }

    @Override
    public QueueDto queue(TextToImageQueueRequest request) {
        val result = webClientUtil.get(URL + "/queue", String.class);
        log.info("队列信息：{}", result);
        val comfyUiQueueResponse = JSONUtil.toBean(result, ComfyUiQueueResponse.class);
        return convertQueueResult(request.getPromptId(), comfyUiQueueResponse);
    }

    /**
     * 文生图片
     *
     * @param request 请求参数
     * @return 执行结果
     */
    @Override
    public PromptDto convertTextToImage(TextToImageRequest request) {
        // 读取工作流文本内容并替换用户输入
        var workflow = workflowProcessService.readWorkflow(SD_TEXT_TO_IMAGE_WORKFLOW);
        // 替换工作流中的参数
        workflow = workflow.replace("{{width}}", request.getWidth().toString())
                .replace("{{height}}", request.getHeight().toString())
                .replace("{{prompt}}", request.getPrompt())
                .replace("{{lora_name}}", request.getStyle().getValue());
        val newWorkflow = workflowProcessService.processWorkflow(workflow, request);
        log.info("convertTextToImage newWorkflow: {}", newWorkflow);
        return execute(newWorkflow, request.getClientId());
    }

    @Override
    public ComfyUiHistoryResponse history(String promptId, String type) {
        if (StrUtil.isEmpty(promptId)) {
            return null;
        }
        // 通过prompt_id获取历史记录
        val result = webClientUtil.get(URL + "/history/" + promptId, String.class);
        log.debug("history result：{}", result);

        if (PromptType.TEXT2IMAGE.getKey().equals(type)) {
            return convertTextToImageHistoryResult(result);
        } else if (PromptType.TEXT2VIDEO.getKey().equals(type)) {
            return convertTextToVideoHistoryResult(result);
        } else if (PromptType.TEXT2MODEL.getKey().equals(type)) {
            return convertTextToModelHistoryResult(result);
        } else if (PromptType.IMAGE2MODEL.getKey().equals(type)) {
            return convertImageToModelHistoryResult(result);
        } else if (PromptType.IMAGE2VIDEO.getKey().equals(type)) {
            return convertImageVideoHistoryResult(result);
        } else {
            log.error(PortalErrorTips.UNKNOWN_PROMPT_TYPE);
        }
        return null;
    }

    /**
     * 文本转视频
     *
     * @param request 请求参数
     * @return 执行结果
     */
    @Override
    public PromptDto convertTextToVideo(TextToVideoRequest request) {
        val workflow = workflowProcessService.readWorkflow(HUNYUAN_TEXT_TO_VIDEO_WORKFLOW);
        val newWorkflow = workflow.replace("{{width}}", request.getWidth().toString())
                .replace("{{height}}", request.getHeight().toString())
                .replace("{{prompt}}", request.getPrompt())
                .replace("{{totalFps}}", String.valueOf(VIDEO_FRAME_RATE * request.getDuration()));
        log.info("newWorkflow: {}", newWorkflow);
        return execute(newWorkflow, request.getClientId());
    }

    /**
     * 取消队列
     */
    @Override
    public ComfyUiQueueResponse cancelQueue(TextToImageQueueCancelRequest request) {
        val promptId = request.getPromptId();
        JSONObject requestBody = new JSONObject();
        if (StrUtil.isNotEmpty(promptId)) {
            requestBody.putOpt("delete", promptId);
        } else {
            requestBody.putOpt("clear", "true");
        }
        val result = webClientUtil.postJSON(URL + "/queue", JSONUtil.toJsonPrettyStr(requestBody), String.class);
        log.info("取消队列结果：{}", result);
        return convertQueueCancelResult(result);
    }

    /**
     * 判断任务是否正在运行
     */
    @Override
    public boolean isRunningQueue(String promptId) {
        val result = webClientUtil.get(URL + "/queue", String.class);
        log.info("队列信息：{}", result);
        val comfyUiTextToImageQueueResponse = JSONUtil.toBean(result, ComfyUiTextToImageQueueResponse.class);
        // 通过任务ID去查看是否有正在运行的任务
        if (!comfyUiTextToImageQueueResponse.getQueueRunning().isEmpty()) {
            return comfyUiTextToImageQueueResponse.getQueueRunning().stream()
                    .anyMatch(item -> promptId.equals((item).get(1)));
        }
        return false;

    }

    /**
     * 中断任务
     */
    @Override
    public boolean interrupt(TextToImageInterruptRequest request) {
        val requestBody = new JSONObject();
        try {
            val result = webClientUtil.postJSON(URL + "/interrupt", JSONUtil.toJsonPrettyStr(requestBody),
                    String.class);
            log.info("中断结果：{}", result);
            return true;
        } catch (Exception e) {
            log.info("中断失败：{}", e.getMessage());
        }
        return false;
    }

    /**
     * 查看图片
     *
     * @param filename 图片文件名
     * @param type     图片类型,目前分为output和temp两种
     * @return 图片字节数组
     */
    public byte[] getOutPutFileData(String filename, String type) {
        try {
            val dataUrl = URL + "/view?filename=" + filename + "&type=" + type;
            return webClientUtil.getForBytes(dataUrl);
        } catch (Exception e) {
            log.error("获取图片数据失败", e);
            return null;
        }
    }

    /**
     * 执行comfyui任务结果转换
     *
     * @param clientId              客户端ID
     * @param comfyUiPromptResponse 执行comfyui任务结果
     * @return 转换后的结果对象
     */
    private PromptDto convertExecuteResult(String clientId, ComfyUiPromptResponse comfyUiPromptResponse) {
        if (CollUtil.isNotEmpty(comfyUiPromptResponse.getNodeErrors())) {
            Asserts.fail(comfyUiPromptResponse.getNodeErrors().toString());
            return null;
        }
        val response = new PromptDto();
        response.setClientId(clientId);
        response.setPromptId(comfyUiPromptResponse.getPromptId());
        val wsUrl = comfyUIProperties.getWsUrl() + "?clientId=" + clientId;
        response.setWsUrl(wsUrl);
        return response;
    }

    private QueueDto convertQueueResult(String promptId,
            ComfyUiQueueResponse comfyUiQueueResponse) {
        val response = new QueueDto();
        // 正常来说正在运行的任务只有一个,所以直接获取第一个
        if (!comfyUiQueueResponse.getQueueRunning().isEmpty()) {
            val runningPromptId = (String) comfyUiQueueResponse.getQueueRunning().get(0).get(1);
            if (promptId.equals(runningPromptId)) {
                response.setPendingNum(0);
            }
        }
        // 从排队的任务中获取查询的promptId信息,获取排队的序号
        if (!comfyUiQueueResponse.getQueuePending().isEmpty()) {
            AtomicInteger pendingNum = new AtomicInteger(0); // 使用 AtomicInteger 来保持索引
            comfyUiQueueResponse.getQueuePending().forEach(item -> {
                pendingNum.incrementAndGet();
                val pendingPromptId = (String) item.get(1);
                if (promptId.equals(pendingPromptId)) {
                    response.setPendingNum(pendingNum.get());
                }
            });
        }
        val totalNum = comfyUiQueueResponse.getQueueRunning().size()
                + comfyUiQueueResponse.getQueuePending().size();
        response.setTotalNum(totalNum);
        return response;
    }

    /**
     * 将历史记录转换为对象
     *
     * @param result 历史记录的字符串
     * @return 历史记录对象
     */
    private ComfyUiHistoryResponse convertTextToImageHistoryResult(String result) {
        // 将字符串解析为 JSON 对象
        val response = new ComfyUiHistoryResponse();
        if (StrUtil.isEmpty(result)) {
            response.setHistoryList(new ArrayList<>());
            return response;
        }
        val jsonData = new JSONObject(result);
        List<HistoryVo> list = new ArrayList<>();
        // 遍历 JSON 数据的每个键
        for (String key : jsonData.keySet()) {
            JSONObject value = jsonData.getJSONObject(key);
            if (!value.containsKey("outputs")) {
                continue;
            }
            // 查找 "outputs" 节点
            JSONObject outputs = value.getJSONObject("outputs");
            // 遍历 "outputs" 的每个子节点
            for (String nodeId : outputs.keySet()) {
                JSONObject outputValue = outputs.getJSONObject(nodeId);
                if (!outputValue.containsKey("images")) {
                    continue;
                }
                JSONArray images = outputValue.getJSONArray("images");
                for (int i = 0; i < images.size(); i++) {
                    JSONObject image = images.getJSONObject(i);
                    if (!image.containsKey("filename")) {
                        continue;
                    }
                    val historyVo = new HistoryVo();
                    val filename = image.getStr("filename");

                    val type = image.getStr("type");
                    // 只要最后一步的输出图片
                    if (!type.equals("output")) {
                        continue;
                    }
                    // 先判断当前图片是否已经存在
                    val suffix = FileUtil.getSuffix(filename);
                    // key是promptId,文件名称按照promptId.后缀的格式保存
                    val dstFileName = (key + "." + suffix).replace("-", "");
                    val dstFilePath = Paths.get(comfyUIProperties.getBasePath(), dstFileName).toString();
                    // 如果文件不存在,则下载图片并保存到本地
                    if (!FileUtil.exist(dstFilePath)) {
                        val imageBytes = getOutPutFileData(filename, type);
                        FileUtil.writeBytes(imageBytes, dstFilePath);
                    }
                    // 这个name到时候需要修改
                    historyVo.setName(filename);
                    historyVo.setPath(dstFilePath);
                    historyVo.setUrl(pathUtils.convertNetUrl(dstFilePath));
                    list.add(historyVo);
                }
            }
        }

        response.setHistoryList(list);
        return response;
    }

    /**
     * 将文生视频的历史记录提取视频结果
     *
     * @param result 历史记录的字符串
     * @return 历史记录对象
     */
    private ComfyUiHistoryResponse convertTextToVideoHistoryResult(String result) {
        // 将字符串解析为 JSON 对象
        val response = new ComfyUiHistoryResponse();
        if (StrUtil.isEmpty(result)) {
            response.setHistoryList(new ArrayList<>());
            return response;
        }
        val jsonData = new JSONObject(result);
        List<HistoryVo> list = new ArrayList<>();
        // 遍历 JSON 数据的每个键
        for (String key : jsonData.keySet()) {
            JSONObject value = jsonData.getJSONObject(key);
            if (!value.containsKey("outputs")) {
                continue;
            }
            // 查找 "outputs" 节点
            JSONObject outputs = value.getJSONObject("outputs");
            // 遍历 "outputs" 的每个子节点
            for (String nodeId : outputs.keySet()) {
                JSONObject outputValue = outputs.getJSONObject(nodeId);
                if (!outputValue.containsKey("gifs")) {
                    continue;
                }
                JSONArray gifs = outputValue.getJSONArray("gifs");
                for (int i = 0; i < gifs.size(); i++) {
                    JSONObject gif = gifs.getJSONObject(i);
                    if (!gif.containsKey("filename")) {
                        continue;
                    }
                    val historyVo = new HistoryVo();
                    val filename = gif.getStr("filename");

                    val type = gif.getStr("type");
                    // 只要最后一步的输出图片
                    if (!type.equals("output")) {
                        continue;
                    }
                    // 先判断当前图片是否已经存在
                    val suffix = FileUtil.getSuffix(filename);
                    // key是promptId,文件名称按照promptId.后缀的格式保存
                    val dstFileName = (key + "." + suffix).replace("-", "");
                    val dstFilePath = Paths.get(comfyUIProperties.getBasePath(), dstFileName).toString();
                    // 如果文件不存在,则下载图片并保存到本地
                    if (!FileUtil.exist(dstFilePath)) {
                        val gifBytes = getOutPutFileData(filename, type);
                        FileUtil.writeBytes(gifBytes, dstFilePath);
                    }
                    // 这个name到时候需要修改
                    historyVo.setName(filename);
                    historyVo.setPath(dstFilePath);
                    historyVo.setUrl(pathUtils.convertNetUrl(dstFilePath));
                    list.add(historyVo);
                }
            }
        }

        response.setHistoryList(list);
        return response;
    }

    /**
     * 将图生视频的历史记录提取视频结果
     *
     * @param result 历史记录的字符串
     * @return 历史记录对象
     */
    private ComfyUiHistoryResponse convertImageVideoHistoryResult(String result) {
        log.info("convertImageVideoHistoryResult result: {}", result);
        // 将字符串解析为 JSON 对象
        val response = new ComfyUiHistoryResponse();
        if (StrUtil.isEmpty(result)) {
            response.setHistoryList(new ArrayList<>());
            return response;
        }
        val jsonData = new JSONObject(result);
        List<HistoryVo> list = new ArrayList<>();
        // 遍历 JSON 数据的每个键
        for (String key : jsonData.keySet()) {
            JSONObject value = jsonData.getJSONObject(key);
            if (!value.containsKey("outputs")) {
                continue;
            }
            // 查找 "outputs" 节点
            JSONObject outputs = value.getJSONObject("outputs");
            // 遍历 "outputs" 的每个子节点
            for (String nodeId : outputs.keySet()) {
                JSONObject outputValue = outputs.getJSONObject(nodeId);
                if (!outputValue.containsKey("gifs")) {
                    continue;
                }
                JSONArray gifs = outputValue.getJSONArray("gifs");
                for (int i = 0; i < gifs.size(); i++) {
                    JSONObject gif = gifs.getJSONObject(i);
                    if (!gif.containsKey("filename")) {
                        continue;
                    }
                    val historyVo = new HistoryVo();
                    val filename = gif.getStr("filename");

                    val type = gif.getStr("type");
                    // 只要最后一步的输出图片
                    if (!type.equals("output")) {
                        continue;
                    }
                    // 先判断当前图片是否已经存在
                    val suffix = FileUtil.getSuffix(filename);
                    // key是promptId,文件名称按照promptId.后缀的格式保存
                    val dstFileName = (key + "." + suffix).replace("-", "");
                    val dstFilePath = Paths.get(comfyUIProperties.getBasePath(), dstFileName).toString();
                    // 如果文件不存在,则下载图片并保存到本地
                    if (!FileUtil.exist(dstFilePath)) {
                        val gifBytes = getOutPutFileData(filename, type);
                        FileUtil.writeBytes(gifBytes, dstFilePath);
                    }
                    // 这个name到时候需要修改
                    historyVo.setName(filename);
                    historyVo.setPath(dstFilePath);
                    historyVo.setUrl(pathUtils.convertNetUrl(dstFilePath));
                    list.add(historyVo);
                }
            }
        }

        response.setHistoryList(list);
        return response;
    }

    /**
     * 将历史记录转换为对象
     *
     * @param result 历史记录的字符串
     * @return 历史记录对象
     */
    private ComfyUiHistoryResponse convertTextToModelHistoryResult(String result) {
        // 将字符串解析为 JSON 对象
        val response = new ComfyUiHistoryResponse();
        if (StrUtil.isEmpty(result)) {
            response.setHistoryList(new ArrayList<>());
            return response;
        }
        val jsonData = new JSONObject(result);
        List<HistoryVo> list = new ArrayList<>();
        // 遍历 JSON 数据的每个键
        for (String key : jsonData.keySet()) {
            JSONObject value = jsonData.getJSONObject(key);
            if (!value.containsKey("outputs")) {
                continue;
            }
            // 查找 "outputs" 节点
            JSONObject outputs = value.getJSONObject("outputs");
            // 遍历 "outputs" 的每个子节点
            for (String nodeId : outputs.keySet()) {
                JSONObject outputValue = outputs.getJSONObject(nodeId);
                if (!outputValue.containsKey("model_file")) {
                    continue;
                }
                JSONArray modelJsonArray = outputValue.getJSONArray("model_file");
                for (int i = 0; i < modelJsonArray.size(); i++) {
                    val filename = modelJsonArray.getStr(i);
                    val historyVo = new HistoryVo();
                    // 先判断当前图片是否已经存在
                    val suffix = FileUtil.getSuffix(filename);
                    // key是promptId,文件名称按照promptId.后缀的格式保存
                    val dstFileName = (key + "." + suffix).replace("-", "");
                    val dstFilePath = Paths.get(comfyUIProperties.getBasePath(), dstFileName).toString();
                    // 如果文件不存在,则下载图片并保存到本地
                    if (FileUtil.exist(dstFilePath)) {
                        continue;
                    }
                    val modelBytes = getOutPutFileData(filename, "output");
                    if (modelBytes != null) {
                        FileUtil.writeBytes(modelBytes, dstFilePath);
                        // 这个name到时候需要修改
                        historyVo.setName(filename);
                        historyVo.setPath(dstFilePath);
                        historyVo.setUrl(pathUtils.convertNetUrl(dstFilePath));
                        list.add(historyVo);
                    }

                }
            }
        }

        response.setHistoryList(list);
        return response;
    }

    /**
     * 将历史记录转换为对象
     *
     * @param result 历史记录的字符串
     * @return 历史记录对象
     */
    private ComfyUiHistoryResponse convertImageToModelHistoryResult(String result) {
        // 将字符串解析为 JSON 对象
        val response = new ComfyUiHistoryResponse();
        if (StrUtil.isEmpty(result)) {
            response.setHistoryList(new ArrayList<>());
            return response;
        }
        val jsonData = new JSONObject(result);
        List<HistoryVo> list = new ArrayList<>();
        // 遍历 JSON 数据的每个键
        for (String key : jsonData.keySet()) {
            JSONObject value = jsonData.getJSONObject(key);
            if (!value.containsKey("outputs")) {
                continue;
            }
            // 查找 "outputs" 节点
            JSONObject outputs = value.getJSONObject("outputs");
            // 遍历 "outputs" 的每个子节点
            for (String nodeId : outputs.keySet()) {
                JSONObject outputValue = outputs.getJSONObject(nodeId);
                if (!outputValue.containsKey("model_file")) {
                    continue;
                }
                JSONArray modelJsonArray = outputValue.getJSONArray("model_file");
                for (int i = 0; i < modelJsonArray.size(); i++) {
                    val filename = modelJsonArray.getStr(i);
                    if (!filename.contains("textured")) {
                        continue;
                    }
                    val historyVo = new HistoryVo();
                    // 先判断当前图片是否已经存在
                    val suffix = FileUtil.getSuffix(filename);
                    // key是promptId,文件名称按照promptId.后缀的格式保存
                    val dstFileName = (key + "." + suffix).replace("-", "");
                    val dstFilePath = Paths.get(comfyUIProperties.getBasePath(), dstFileName).toString();
                    // 如果文件不存在,则下载图片并保存到本地
                    if (FileUtil.exist(dstFilePath)) {
                        continue;
                    }
                    log.info("getOutPutFileData filename: {}", filename);
                    val modelBytes = getOutPutFileData(filename, "output");
                    if (modelBytes != null) {
                        FileUtil.writeBytes(modelBytes, dstFilePath);
                        // 这个name到时候需要修改
                        historyVo.setName(filename);
                        historyVo.setPath(dstFilePath);
                        historyVo.setUrl(pathUtils.convertNetUrl(dstFilePath));
                        list.add(historyVo);
                    }

                }
            }
        }

        response.setHistoryList(list);
        return response;
    }

    private ComfyUiQueueResponse convertQueueCancelResult(String result) {
        return null;
    }

    @Override
    public UploadImageDto uploadImage(File file) {
        try {
            // 创建 MultiValueMap 来构建 multipart 请求
            MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();

            // 将 File 转换为 Resource
            Resource resource = new ByteArrayResource(FileUtil.readBytes(file)) {
                @Override
                public String getFilename() {
                    return file.getName();
                }
            };

            // 添加文件
            formData.add("image", resource);

            // 发送请求
            String result = webClientUtil.postFormData(URL + "/upload/image", formData, String.class);
            log.info("上传图片结果：{}", result);

            // 解析响应
            val response = JSONUtil.toBean(result, ComfyUiUploadImageResponse.class);
            // 返回结果
            val uploadImageDto = new UploadImageDto();
            uploadImageDto.setName(response.getName());
            return uploadImageDto;
        } catch (Exception e) {
            log.error("上传图片失败", e);
            return null;
        }
    }

    @Override
    public PromptDto convertImageToModel(ImageToModelRequest request, File file) {
        val uploadImageDto = uploadImage(file);
        if (uploadImageDto == null) {
            log.error("comfyui 上传图片失败");
            Asserts.fail(PortalErrorTips.UPLOAD_IMAGE_FAILED);
            return null;
        }
        val name = uploadImageDto.getName();
        val workflow = workflowProcessService.readWorkflow(HUNYUAN_IMAGE_TO_MODEL_WORKFLOW);
        if (StrUtil.isEmpty(workflow)) {
            log.error("comfyui 读取工作流失败");
            Asserts.fail(PortalErrorTips.READ_WORKFLOW_FAILED);
            return null;
        }
        // 目前只需要替换{{image}}
        val newWorkflow = workflow.replace("{{image}}", name);
        log.info("convertImageToModel newWorkflow: {}", newWorkflow);
        return execute(newWorkflow, request.getClientId());
    }

    @Override
    public PromptDto convertTextToModel(TextToModelRequest param) {
        // 目前只需要替换{{image}}
        val workflow = workflowProcessService.readWorkflow(HUNYUAN_TEXT_TO_MODEL_WORKFLOW);
        val newWorkflow = workflow.replace("{{prompt}}", param.getPrompt());
        log.info("convertTextToModel newWorkflow: {}", newWorkflow);
        return execute(newWorkflow, param.getClientId());
    }

    @Override
    public PromptDto convertImageVideo(ImageToVideoRequest param, File file) {
        val uploadImageDto = uploadImage(file);
        if (uploadImageDto == null) {
            log.error("comfyui 上传图片失败");
            Asserts.fail(PortalErrorTips.UPLOAD_IMAGE_FAILED);
            return null;
        }
        val name = uploadImageDto.getName();
        val workflow = workflowProcessService.readWorkflow(WAN_IMAGE_TO_VIDEO_WORKFLOW);
        if (StrUtil.isEmpty(workflow)) {
            log.error("comfyui 读取工作流失败");
            Asserts.fail(PortalErrorTips.READ_WORKFLOW_FAILED);
            return null;
        }
        // 目前只需要替换{{image}} 和 提示词部分
        var newWorkflow = workflow.replace("{{image}}", name)
                .replace("{{prompt}}", param.getPrompt());

        // 如果宽高不为空,则替换宽高
        if (param.getWidth() != null && param.getHeight() != null) {
            newWorkflow = newWorkflow.replace("{{width}}", param.getWidth().toString())
                    .replace("{{height}}", param.getHeight().toString());
        }
        // 如果时长不为空,则替换时长
        if (param.getDuration() != null) {
            // 根据当前时长和fps来计算总的fps
            val totalFps = param.getDuration() * VIDEO_FRAME_RATE;
            newWorkflow = newWorkflow.replace("{{totalFps}}", String.valueOf(totalFps));
        }
        log.info("convertImageVideo newWorkflow: {}", newWorkflow);
        return execute(newWorkflow, param.getClientId());
    }

}
